home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Event / Dispatcher.php next >
Encoding:
PHP Script  |  2005-12-02  |  17.0 KB  |  478 lines

  1. <?php
  2. // +-----------------------------------------------------------------------+
  3. // | Copyright (c) 2005, Bertrand Mansion                                  |
  4. // | All rights reserved.                                                  |
  5. // |                                                                       |
  6. // | Redistribution and use in source and binary forms, with or without    |
  7. // | modification, are permitted provided that the following conditions    |
  8. // | are met:                                                              |
  9. // |                                                                       |
  10. // | o Redistributions of source code must retain the above copyright      |
  11. // |   notice, this list of conditions and the following disclaimer.       |
  12. // | o Redistributions in binary form must reproduce the above copyright   |
  13. // |   notice, this list of conditions and the following disclaimer in the |
  14. // |   documentation and/or other materials provided with the distribution.|
  15. // | o The names of the authors may not be used to endorse or promote      |
  16. // |   products derived from this software without specific prior written  |
  17. // |   permission.                                                         |
  18. // |                                                                       |
  19. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
  20. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
  21. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
  23. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
  25. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
  30. // |                                                                       |
  31. // +-----------------------------------------------------------------------+
  32. // | Author: Bertrand Mansion <bmansion@mamasam.com>                       |
  33. // |         Stephan Schmidt <schst@php.net>                               |
  34. // +-----------------------------------------------------------------------+
  35. //
  36. // $Id: Dispatcher.php,v 1.4 2005/09/22 15:37:10 schst Exp $
  37.  
  38. require_once 'Event/Notification.php';
  39.  
  40. /**
  41.  * Pseudo 'static property' for Notification object
  42.  * @global array $GLOBALS["_Event_Dispatcher"]
  43.  */
  44. $GLOBALS['_Event_Dispatcher'] = array(
  45.                                   'NotificationClass' => 'Event_Notification'
  46.                                      );
  47.  
  48. /**
  49.  * Registers a global observer
  50.  */
  51. define('EVENT_DISPATCHER_GLOBAL', '');
  52.  
  53. /**
  54.  * Dispatch notifications using PHP callbacks
  55.  *
  56.  * The Event_Dispatcher acts acts as a notification dispatch table.
  57.  * It is used to notify other objects of interesting things, if
  58.  * they meet certain criteria. This information is encapsulated 
  59.  * in {@link Event_Notification} objects. Client objects register 
  60.  * themselves with the Event_Dispatcher as observers of specific
  61.  * notifications posted by other objects. When an event occurs,
  62.  * an object posts an appropriate notification to the Event_Dispatcher.
  63.  * The Event_Dispatcher dispatches a message to each
  64.  * registered observer, passing the notification as the sole argument.
  65.  *
  66.  * The Event_Dispatcher is actually a combination of three design
  67.  * patterns: the Singleton, {@link http://c2.com/cgi/wiki?MediatorPattern Mediator},
  68.  * and Observer patterns. The idea behind Event_Dispatcher is borrowed from 
  69.  * {@link http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/index.html Apple's Cocoa framework}.
  70.  *
  71.  * @category   Event
  72.  * @package    Event_Dispatcher
  73.  * @author     Bertrand Mansion <bmansion@mamasam.com>
  74.  * @author     Stephan Schmidt <schst@php.net>
  75.  * @copyright  1997-2005 The PHP Group
  76.  * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
  77.  * @version    Release: 1.0.0
  78.  * @link       http://pear.php.net/package/Event_Dispatcher
  79.  */
  80. class Event_Dispatcher
  81. {
  82.     /**
  83.      * Registered observer callbacks
  84.      * @var array
  85.      * @access private
  86.      */
  87.     var $_ro = array();
  88.     
  89.     /**
  90.      * Pending notifications
  91.      * @var array
  92.      * @access private
  93.      */
  94.     var $_pending = array();
  95.  
  96.     /**
  97.      * Nested observers
  98.      * @var array
  99.      * @access private
  100.      */
  101.     var $_nestedDispatchers = array();
  102.  
  103.     /**
  104.      * Name of the dispatcher
  105.      * @var string
  106.      * @access private
  107.      */
  108.     var $_name = null;
  109.  
  110.     /**
  111.      * Class used for notifications
  112.      * @var string
  113.      * @access private
  114.      */
  115.     var $_notificationClass = null;
  116.  
  117.     /**
  118.      * PHP4 constructor
  119.      *
  120.      * Please use {@link getInstance()} instead.
  121.      *
  122.      * @access  private
  123.      * @param   string      Name of the notification dispatcher.
  124.      */
  125.     function Event_Dispatcher($name)
  126.     {
  127.         Event_Dispatcher::__construct($name);
  128.     }
  129.  
  130.     /**
  131.      * PHP5 constructor
  132.      *
  133.      * Please use {@link getInstance()} instead.
  134.      *
  135.      * @access  private
  136.      * @param   string      Name of the notification dispatcher.
  137.      */
  138.     function __construct($name)
  139.     {
  140.         $this->_name = $name;
  141.         $this->_notificationClass = $GLOBALS['_Event_Dispatcher']['NotificationClass'];
  142.     }
  143.  
  144.     /**
  145.      * Returns a notification dispatcher singleton
  146.      *
  147.      * There is usually no need to have more than one notification
  148.      * center for an application so this is the recommended way
  149.      * to get a Event_Dispatcher object.
  150.      *
  151.      * @param string    Name of the notification dispatcher.
  152.      *                  The default notification dispatcher is named __default.
  153.      * 
  154.      * @return object Event_Dispatcher
  155.      */
  156.     function &getInstance($name = '__default')
  157.     {
  158.         static $dispatchers = array();
  159.  
  160.         if (!isset($dispatchers[$name])) {
  161.             $dispatchers[$name] = new Event_Dispatcher($name);
  162.         }
  163.  
  164.         return $dispatchers[$name];
  165.     }
  166.  
  167.     /**
  168.      * Registers an observer callback
  169.      *
  170.      * This method registers a {@link http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback callback}
  171.      * which is called when the notification corresponding to the
  172.      * criteria given at registration time is posted.
  173.      * The criteria are the notification name and eventually the 
  174.      * class of the object posted with the notification.
  175.      *
  176.      * If there are any pending notifications corresponding to the criteria
  177.      * given here, the callback will be called straight away.
  178.      *
  179.      * If the notification name is empty, the observer will receive all the
  180.      * posted notifications. Same goes for the class name.
  181.      *
  182.      * @access  public
  183.      * @param   mixed       A PHP callback
  184.      * @param   string      Expected notification name, serves as a filter
  185.      * @param   string      Expected contained object class, serves as a filter
  186.      * @return void
  187.      */
  188.     function addObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
  189.     {
  190.         if (is_array($callback)) {
  191.             if (is_object($callback[0])) {
  192.                 // Note : PHP4 does not allow correct object comparison so
  193.                 // only the class name is used for registration checks.
  194.                 $reg = get_class($callback[0]).'::'.$callback[1];
  195.             } else {
  196.                 $reg = $callback[0].'::'.$callback[1];
  197.             }
  198.         } else {
  199.             $reg = $callback;
  200.         }
  201.  
  202.         $this->_ro[$nName][$reg] = array(
  203.                                     'callback' => $callback,
  204.                                     'class'    => $class
  205.                                     );
  206.  
  207.         // Post eventual pending notifications for this observer
  208.         if (isset($this->_pending[$nName])) {
  209.             foreach (array_keys($this->_pending[$nName]) as $k) {
  210.                 $notification =& $this->_pending[$nName][$k];
  211.                 if (!$notification->isNotificationCancelled()) {
  212.                     $objClass = get_class($notification->getNotificationObject());
  213.                     if (empty($class) || strcasecmp($class, $objClass) == 0) {
  214.                         call_user_func_array($callback, array(&$notification));
  215.                         $notification->increaseNotificationCount();
  216.                     }
  217.                 }
  218.             }
  219.         }
  220.     }
  221.  
  222.     /**
  223.      * Creates and posts a notification object
  224.      *
  225.      * The purpose of the optional associated object is generally to pass
  226.      * the object posting the notification to the observers, so that the 
  227.      * observers can query the posting object for more information about
  228.      * the event.
  229.      *
  230.      * Notifications are by default added to a pending notification list.
  231.      * This way, if an observer is not registered by the time they are 
  232.      * posted, it will still be notified when it is added as an observer.
  233.      * This behaviour can be turned off in order to make sure that only
  234.      * the registered observers will be notified.
  235.      *
  236.      * The info array serves as a container for any kind of useful 
  237.      * information. It is added to the notification object and posted along.
  238.      *
  239.      * @access  public
  240.      * @param   object      Notification associated object
  241.      * @param   string      Notification name
  242.      * @param   array       Optional user information
  243.      * @param   bool        Whether the notification is pending
  244.      * @param   bool        Whether you want the notification to bubble up
  245.      * @return  object  The notification object
  246.      */
  247.     function &post(&$object, $nName, $info = array(), $pending = true, $bubble = true)
  248.     {
  249.         $notification =& new $this->_notificationClass($object, $nName, $info);
  250.         return $this->postNotification($notification, $pending, $bubble);
  251.     }
  252.  
  253.     /**
  254.      * Posts the {@link Event_Notification} object
  255.      *
  256.      * @access  public
  257.      * @param   object      The Notification object
  258.      * @param   bool        Whether to post the notification immediately
  259.      * @param   bool        Whether you want the notification to bubble up
  260.      * @see Event_Dispatcher::post()
  261.      * @return  object  The notification object
  262.      */
  263.     function &postNotification(&$notification, $pending = true, $bubble = true)
  264.     {
  265.         $nName = $notification->getNotificationName();
  266.         if ($pending === true) {
  267.             $this->_pending[$nName][] =& $notification;
  268.         }
  269.         $objClass = get_class($notification->getNotificationObject());
  270.  
  271.         // Find the registered observers
  272.         if (isset($this->_ro[$nName])) {
  273.             foreach (array_keys($this->_ro[$nName]) as $k) {
  274.                 $rObserver =& $this->_ro[$nName][$k];
  275.                 if ($notification->isNotificationCancelled()) {
  276.                     return $notification;
  277.                 }
  278.                 if (empty($rObserver['class']) ||
  279.                     strcasecmp($rObserver['class'], $objClass) == 0) {
  280.                     call_user_func_array($rObserver['callback'], array(&$notification));
  281.                     $notification->increaseNotificationCount();
  282.                 }
  283.             }
  284.         }
  285.  
  286.         // Notify globally registered observers
  287.         if (isset($this->_ro[EVENT_DISPATCHER_GLOBAL])) {
  288.             foreach (array_keys($this->_ro[EVENT_DISPATCHER_GLOBAL]) as $k) {
  289.                 $rObserver =& $this->_ro[EVENT_DISPATCHER_GLOBAL][$k];
  290.                 if ($notification->isNotificationCancelled()) {
  291.                     return $notification;
  292.                 }
  293.                 if (empty($rObserver['class']) || 
  294.                     strcasecmp($rObserver['class'], $objClass) == 0) {
  295.                     call_user_func_array($rObserver['callback'], array(&$notification));
  296.                     $notification->increaseNotificationCount();
  297.                 }
  298.             }
  299.         }
  300.  
  301.         if ($bubble === false) {
  302.             return $notification;
  303.         }
  304.         
  305.         // Notify in nested dispatchers
  306.         foreach (array_keys($this->_nestedDispatchers) as $nested) {
  307.             $notification =& $this->_nestedDispatchers[$nested]->postNotification($notification, $pending);
  308.         }
  309.  
  310.         return $notification;
  311.     }
  312.  
  313.     /**
  314.      * Removes a registered observer that correspond to the given criteria
  315.      *
  316.      * @access  public
  317.      * @param   mixed       A PHP callback
  318.      * @param   string      Notification name
  319.      * @param   string      Contained object class
  320.      * @return  bool    True if an observer was removed, false otherwise
  321.      */
  322.     function removeObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
  323.     {
  324.         if (is_array($callback)) {
  325.             if (is_object($callback[0])) {
  326.                 $reg = get_class($callback[0]).'::'.$callback[1];
  327.             } else {
  328.                 $reg = $callback[0].'::'.$callback[1];
  329.             }
  330.         } else {
  331.             $reg = $callback;
  332.         }
  333.  
  334.         $removed = false;
  335.         if (isset($this->_ro[$nName][$reg])) {
  336.             if (!empty($class)) {
  337.                 if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) {
  338.                     unset($this->_ro[$nName][$reg]);
  339.                     $removed = true;
  340.                 }
  341.             } else {
  342.                 unset($this->_ro[$nName][$reg]);
  343.                 $removed = true;
  344.             }
  345.         }
  346.  
  347.         if (isset($this->_ro[$nName]) && count($this->_ro[$nName]) == 0) {
  348.             unset($this->_ro[$nName]);
  349.         }
  350.         return $removed;
  351.     }
  352.  
  353.    /**
  354.     * Check, whether the specified observer has been registered with the
  355.     * dispatcher
  356.     *
  357.      * @access  public
  358.      * @param   mixed       A PHP callback
  359.      * @param   string      Notification name
  360.      * @param   string      Contained object class
  361.      * @return  bool        True if the observer has been registered, false otherwise
  362.     */
  363.     function observerRegistered($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
  364.     {
  365.         if (is_array($callback)) {
  366.             if (is_object($callback[0])) {
  367.                 $reg = get_class($callback[0]).'::'.$callback[1];
  368.             } else {
  369.                 $reg = $callback[0].'::'.$callback[1];
  370.             }
  371.         } else {
  372.             $reg = $callback;
  373.         }
  374.  
  375.         if (!isset($this->_ro[$nName][$reg])) {
  376.             return false;
  377.         }
  378.         if (empty($class)) {
  379.             return true;
  380.         }
  381.         if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) {
  382.             return true;
  383.         }
  384.         return false;
  385.     }
  386.  
  387.    /**
  388.     * Get all observers, that have been registered for a notification
  389.     *
  390.      * @access  public
  391.      * @param   string      Notification name
  392.      * @param   string      Contained object class
  393.      * @return  array       List of all observers
  394.     */
  395.     function getObservers($nName = EVENT_DISPATCHER_GLOBAL, $class = null)
  396.     {
  397.         $observers = array();        
  398.         if (!isset($this->_ro[$nName])) {
  399.             return $observers;
  400.         }
  401.         foreach ($this->_ro[$nName] as $reg => $observer) {
  402.             if ($class == null || $observer['class'] == null ||  strcasecmp($observer['class'], $class) == 0) {
  403.                 $observers[] = $reg;
  404.             }
  405.         }
  406.         return $observers;
  407.     }
  408.     
  409.     /**
  410.      * Get the name of the dispatcher.
  411.      *
  412.      * The name is the unique identifier of a dispatcher.
  413.      *
  414.      * @access   public
  415.      * @return   string     name of the dispatcher
  416.      */
  417.     function getName()
  418.     {
  419.         return $this->_name;
  420.     }
  421.  
  422.     /**
  423.      * add a new nested dispatcher
  424.      *
  425.      * Notifications will be broadcasted to this dispatcher as well, which
  426.      * allows you to create event bubbling.
  427.      *
  428.      * @access   public
  429.      * @param    Event_Dispatcher    The nested dispatcher
  430.      */
  431.     function addNestedDispatcher(&$dispatcher)
  432.     {
  433.         $name = $dispatcher->getName();
  434.         $this->_nestedDispatchers[$name] =& $dispatcher;
  435.     }
  436.  
  437.    /**
  438.     * remove a nested dispatcher
  439.     *
  440.     * @access   public
  441.     * @param    Event_Dispatcher    Dispatcher to remove
  442.     * @return   boolean
  443.     */
  444.     function removeNestedDispatcher($dispatcher)
  445.     {
  446.         if (is_object($dispatcher)) {
  447.             $dispatcher = $dispatcher->getName();
  448.         }
  449.         if (!isset($this->_nestedDispatchers[$dispatcher])) {
  450.             return false;
  451.         }
  452.         unset($this->_nestedDispatchers[$dispatcher]);
  453.         return true;
  454.     }
  455.  
  456.     /**
  457.      * Changes the class used for notifications
  458.      *
  459.      * You may call this method on an object to change it for a single
  460.      * dispatcher or statically, to set the default for all dispatchers
  461.      * that will be created.
  462.      *
  463.      * @access   public
  464.      * @param    string     name of the notification class
  465.      * @return   boolean
  466.      */
  467.     function setNotificationClass($class)
  468.     {
  469.         if (isset($this) && is_a($this, 'Event_Dispatcher')) {
  470.             $this->_notificationClass = $class;
  471.             return true;
  472.         }
  473.         $GLOBALS['_Event_Dispatcher']['NotificationClass'] = $class;
  474.         return true;
  475.     }
  476.  
  477. }
  478. ?>